import os
import sys
import subprocess
import json
import random
import re
from pathlib import Path
from datetime import datetime
import traceback
from dotenv import load_dotenv

# 加载.env文件中的环境变量
load_dotenv()

# 设置标准输出和标准错误的编码为UTF-8
sys.stdout.reconfigure(encoding='utf-8')
sys.stderr.reconfigure(encoding='utf-8')


def transcode_video_for_recording(input_path, output_path):
    """
    将视频转码为更适合录制的格式，并添加视频滤镜效果
    """
    cmd = [
        'ffmpeg', '-y', '-i', str(input_path),
        '-vf', 'setpts=PTS+0.05/TB,eq=brightness=0.02:contrast=1.03,boxblur=1:1',
        '-af', 'atempo=1.0',
        '-c:v', 'libx264', '-preset', 'medium', '-crf', '23',
        '-c:a', 'aac', '-b:a', '128k',
        '-movflags', '+faststart',
        '-map_metadata', '-1',
        str(output_path)
    ]
    
    try:
        result = subprocess.run(
            cmd,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True,
            timeout=300,  # 转码可能需要更长时间
            encoding='utf-8',
            errors='replace'
        )
        return result.returncode == 0
    except Exception as e:
        print(f"视频转码失败: {e}")
        return False


def get_audio_duration(audio_path):
    """
    获取音频文件的时长(秒)
    """
    # 确保路径是字符串格式
    audio_path_str = str(audio_path)
    
    cmd = [
        'ffprobe', '-v', 'error', '-show_entries', 'format=duration',
        '-of', 'default=noprint_wrappers=1:nokey=1', audio_path_str
    ]
    
    try:
        result = subprocess.run(
            cmd, 
            stdout=subprocess.PIPE, 
            stderr=subprocess.PIPE, 
            text=True, 
            timeout=30,
            encoding='utf-8',
            errors='replace'  # 替换无法解码的字符
        )
        duration = float(result.stdout.strip())
        return min(duration, 10.0)  # 限制最大时长为10秒
    except Exception as e:
        print(f"获取音频时长失败 {audio_path}: {e}")
        return None


def get_max_image_dimensions(image_files):
    """
    获取图片文件列表中的最大宽度和最大高度
    """
    max_width = 0
    max_height = 0
    
    for image_file in image_files:
        cmd = [
            'ffprobe', '-v', 'error', '-show_entries', 'stream=width,height',
            '-of', 'csv=p=0:s=x', str(image_file)
        ]
        
        try:
            result = subprocess.run(
                cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,
                timeout=10,
                encoding='utf-8',
                errors='replace'
            )
            
            if result.returncode == 0:
                # 解析输出，格式为 "width x height\n"
                dimensions = result.stdout.strip().split('x')
                if len(dimensions) >= 2:
                    width = int(dimensions[0])
                    height = int(dimensions[1])
                    max_width = max(max_width, width)
                    max_height = max(max_height, height)
        except Exception as e:
            print(f"获取图片尺寸失败 {image_file}: {e}")
    
    # 如果没有获取到尺寸，则使用默认值
    if max_width == 0 or max_height == 0:
        max_width = 1920
        max_height = 1080
        
    return max_width, max_height


def create_video(images_dir, audio_file, output_path):
    """
    使用图片和音频创建视频
    """
    # 获取所有图片文件
    image_files = []
    for ext in ('*.png', '*.jpg', '*.jpeg', '*.bmp'):
        image_files.extend(Path(images_dir).glob(ext))
    
    image_files = sorted([str(f) for f in image_files])
    
    if not image_files:
        return False
    
    print(f"在目录 {images_dir} 中找到 {len(image_files)} 张图片")
    
    # 获取图片的最大宽度和高度
    max_width, max_height = get_max_image_dimensions(image_files)
    print(f"检测到的最大图片尺寸: {max_width}x{max_height}")
    
    # 获取音频时长
    audio_duration = get_audio_duration(audio_file)
    if audio_duration is None:
        print(f"无法获取音频时长: {audio_file}")
        return False
    
    # 计算每张图片的展示时间 = 音频时长 / 图片数量
    num_images = len(image_files)
    duration_per_image = audio_duration / num_images
    
    print(f"音频总时长: {audio_duration:.2f}秒")
    print(f"图片数量: {num_images}")
    print(f"每张图片展示时间: {duration_per_image:.2f}秒")
    
    # 创建临时工作目录在本地，避免UNC路径问题
    temp_work_dir = Path.cwd() / "temp_ffmpeg"
    temp_work_dir.mkdir(exist_ok=True)
    
    # 复制图片到临时工作目录
    temp_image_files = []
    for image_file in image_files:
        import shutil
        image_path = Path(image_file)
        temp_image_path = temp_work_dir / image_path.name
        shutil.copy2(image_path, temp_image_path)
        temp_image_files.append(temp_image_path)
    
    # 使用ffmpeg创建视频
    temp_video = temp_work_dir / "temp_output.mp4"
    
    # 确保输出目录存在
    Path(output_path).parent.mkdir(parents=True, exist_ok=True)
    
    # 切换到临时工作目录执行命令
    old_cwd = os.getcwd()
    os.chdir(temp_work_dir)
    
    try:
        # 构建命令 - 使用-loop和-filter_complex方式
        cmd = ['ffmpeg', '-y']
        
        # 为每张图片添加输入参数
        for image_file in temp_image_files:
            cmd.extend(['-loop', '1', '-t', str(duration_per_image), '-i', image_file.name])
        
        # 添加filter_complex参数
        filter_complex = ''
        for i in range(len(temp_image_files)):
            # 添加setsar过滤器来统一样本宽高比
            filter_complex += f'[{i}:v]scale={max_width}:{max_height}:force_original_aspect_ratio=decrease,pad={max_width}:{max_height}:(ow-iw)/2:(oh-ih)/2,setsar=1[v{i}];'
        
        # 连接所有视频片段
        concat_inputs = ''.join([f'[v{i}]' for i in range(len(temp_image_files))])
        filter_complex += f'{concat_inputs}concat=n={len(temp_image_files)}:v=1[outv]'
        
        cmd.extend([
            '-filter_complex', filter_complex,
            '-map', '[outv]',
            '-vsync', 'vfr',
            '-pix_fmt', 'yuv420p', 'temp_output.mp4'
        ])
        
        # 打印FFmpeg命令用于调试
        print(f"执行命令: {' '.join(cmd)}")
        
        result = subprocess.run(
            cmd, 
            capture_output=True, 
            text=True, 
            timeout=audio_duration*3,
            encoding='utf-8',
            errors='replace'  # 替换无法解码的字符
        )
        if result.returncode != 0:
            print(f"FFmpeg stderr: {result.stderr}")
            print(f"FFmpeg stdout: {result.stdout}")
            raise subprocess.CalledProcessError(result.returncode, cmd)
    except subprocess.TimeoutExpired:
        print("视频创建超时")
        os.chdir(old_cwd)
        # 清理临时文件
        import shutil
        shutil.rmtree(temp_work_dir, ignore_errors=True)
        return False
    except subprocess.CalledProcessError as e:
        print(f"创建视频失败: {e}")
        print(f"错误详情: {e.stderr if hasattr(e, 'stderr') else '无详细错误信息'}")
        os.chdir(old_cwd)
        # 清理临时文件
        import shutil
        shutil.rmtree(temp_work_dir, ignore_errors=True)
        return False
    finally:
        os.chdir(old_cwd)
    
    # 添加音频到视频，但只使用前10秒
    final_output = Path(output_path)
    cmd = [
        'ffmpeg', '-y', '-i', str(temp_video),
        '-ss', '0', '-t', '10', '-i', str(audio_file), '-c:v', 'copy', '-c:a', 'aac', '-b:a', '192k', str(final_output)
    ]
    
    # 打印FFmpeg命令用于调试
    print(f"执行命令: {' '.join(cmd)}")
    
    try:
        result = subprocess.run(
            cmd, 
            capture_output=True, 
            text=True, 
            timeout=audio_duration*3,
            encoding='utf-8',
            errors='replace'  # 替换无法解码的字符
        )
        if result.returncode != 0:
            print(f"FFmpeg stderr: {result.stderr}")
            print(f"FFmpeg stdout: {result.stdout}")
            raise subprocess.CalledProcessError(result.returncode, cmd)
        
        print(f"成功创建视频: {final_output}")
        
        # 视频转码为更适合录制的格式
        transcoded_video = Path(output_path).with_suffix('.transcoded.mp4')
        if transcode_video_for_recording(str(final_output), str(transcoded_video)):
            # 转码成功，替换原文件
            import shutil
            shutil.move(str(transcoded_video), str(output_path))
            print(f"视频已转码为录制格式: {output_path}")
        else:
            print("视频转码失败，使用原始格式")
        
        result = True
    except subprocess.TimeoutExpired:
        print("添加音频到视频超时")
        result = False
    except subprocess.CalledProcessError as e:
        print(f"添加音频到视频失败: {e}")
        print(f"错误详情: {e.stderr if hasattr(e, 'stderr') else '无详细错误信息'}")
        result = False
    finally:
        # 清理临时文件
        import shutil
        shutil.rmtree(temp_work_dir, ignore_errors=True)
    
    return result


def process_directories(images_base_dir, audio_base_dir, output_base_dir):
    """
    处理目录结构并生成视频
    """
    # 检查输入目录是否存在
    images_base_path = Path(images_base_dir)
    audio_base_path = Path(audio_base_dir)
    
    if not images_base_path.exists():
        print(f"图片目录不存在: {images_base_dir}")
        return
        
    if not audio_base_path.exists():
        print(f"音频目录不存在: {audio_base_dir}")
        return
    
    # 确保输出目录存在
    Path(output_base_dir).mkdir(parents=True, exist_ok=True)
    
    # 获取音频文件
    audio_files = []
    for ext in ('*.mp3', '*.wav', '*.aac', '*.flac', '*.m4a'):
        audio_files.extend(audio_base_path.glob(ext))
    
    if not audio_files:
        print(f"在 {audio_base_dir} 中未找到音频文件")
        return
    
    print(f"找到 {len(audio_files)} 个音频文件")
    
    # 获取所有日期目录并排序
    date_dirs = []
    for item in images_base_path.iterdir():
        if item.is_dir() and re.match(r'^\d{8}$', item.name):
            date_dirs.append(item)
    
    # 按日期排序
    date_dirs.sort(key=lambda x: x.name)
    
    # 遍历图片目录 YYYYMMDD/NNN/
    processed_count = 0
    total_dirs = 0
    
    for date_dir in date_dirs:
        # 获取所有子目录并排序
        sub_dirs = []
        for item in date_dir.iterdir():
            if item.is_dir():
                sub_dirs.append(item)
        
        # 按名称排序
        sub_dirs.sort(key=lambda x: x.name)
        
        for sub_dir in sub_dirs:
            total_dirs += 1
            
            # 获取图片目录
            images_dir = sub_dir
            
            # 检查目录中是否有图片
            image_files = []
            for ext in ('*.png', '*.jpg', '*.jpeg', '*.bmp'):
                image_files.extend(Path(images_dir).glob(ext))
                
            # 如果已有视频文件则跳过
            output_file = Path(output_base_dir) / date_dir.name / f"{sub_dir.name}.mp4"
            if output_file.exists():
                continue
            
            # 只有找到图片才处理和显示
            if image_files:
                # 输出正在处理的目录
                print(f"处理目录 {total_dirs}: {date_dir.name}/{sub_dir.name}")
                
                # 随机选择一个音频文件
                selected_audio = random.choice(audio_files)
                print(f"为目录 {sub_dir.name} 随机选择音频文件: {selected_audio.name}")
                
                # 创建输出目录
                output_file.parent.mkdir(parents=True, exist_ok=True)
                
                # 创建视频
                print(f"正在处理: {images_dir.name} + {selected_audio.name} -> {output_file}")
                if create_video(str(images_dir), selected_audio, str(output_file)):
                    processed_count += 1
    
    print(f"处理完成，共扫描 {total_dirs} 个目录，生成 {processed_count} 个视频")


def main():
    # 如果使用命令行参数
    if len(sys.argv) != 4:
        print("用法: python video_generator.py <图片基础目录> <音频目录> <输出目录>")
        print("示例: python video_generator.py ./images ./audios ./output")
        return
    
    images_base_dir = sys.argv[1]
    audio_base_dir = sys.argv[2]
    output_base_dir = sys.argv[3]
    
    process_directories(images_base_dir, audio_base_dir, output_base_dir)


if __name__ == "__main__":
    main()